﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

namespace PolarSonicLink
{
    [Serializable()]
    public class ByteAssembler
    {
        [DllImport("DSPLib.dll")]
        public static extern ushort CalcCRC(ushort currentCRC, byte crcByte);

        public enum MonitorTypeEnum
        {
            RS200 = 1,
            S410 = 10,
            S510 = 81
        }

        public ByteAssembler()
        {
            FinishedDecoding = false;
            Restart = 1;
            Padding = false;
            CRC = 0;
            SectionStart = 8;
            SectionNumber = 1;

            AssembledBytes = 0;

            if ( AssembledData == null )
            {
                AssembledData = new List<int>();
            }
        }


        public void AddByte(int thisByte, UserInterface.UpdateStatusCallback UpdateStatus, UserInterface.UpdateProgressCallback UpdateProgress)
        {
            if ( AssembledBytes == 0 )
            {
                UpdateProgress(0);
            }

            if ( Padding == false )
            {
                AssembledData.Add(thisByte);
            }

            AssembledBytes++;

            if (SectionHeaderCount < 3) {
                SectionHeaders[SectionHeaderCount++] = thisByte;
			}

            CRC = CalcCRC(CRC, (byte) thisByte);

            if ( AssembledBytes == 1 && AssembledData[0] != 85 )
            {
                UpdateStatus("Bad first section header (byte 1).");
                FinishedDecoding = true;
            }
            else if ( AssembledBytes == 2)
            {
                switch ( (MonitorTypeEnum) AssembledData[1] )
                {
                    case MonitorTypeEnum.RS200:
                        MonitorType = MonitorTypeEnum.RS200;
                        UpdateStatus("Found RS200 Monitor type.");
                        break;
                    default:
                        throw new NotImplementedException("Monitor Type not implemented");
                        break;
                }
            }
            else if ( AssembledBytes == 3 && AssembledData[2] != 2 )
            {
                UpdateStatus("Bad preamble header.");
                FinishedDecoding = true;
            }
            else if ( AssembledBytes == 4 )
            {
                // byte 3 stores the number of sections
                NumberOfSections = AssembledData[3];
                UpdateStatus(String.Format("Number of sections in file is {0}.", NumberOfSections));
            }
            else if ( AssembledBytes == 6 )
            {
                // bytes 4 and 5 give the size of how much actual data there is in sections (excluding padding)
                // add size of main header + section headers + 1 for trailer to get total
                DataSize = ( AssembledData[5] << 8 ) + AssembledData[4] + 8 + 3 * NumberOfSections + 1;

                // file size is header + 256 bytes per section + trailer
                FileSize = 8 + NumberOfSections * RS200_SECTION_LENGTH + 1;
            }
            else if ( AssembledBytes == 8 && CRC > 0)
            {
                UpdateStatus("Preamble crc error.");
                FinishedDecoding = true;
            }
            else if ( AssembledBytes == SectionStart )
            {
                SectionHeaderCount = 0;
            }
            else if ( AssembledBytes == SectionStart + 3 )
            {
                if ( SectionHeaders[0] != 85 || SectionHeaders[1] != SectionNumber || SectionHeaders[2] <= 0 )
                {
                    UpdateStatus("Bad section header.");
                    FinishedDecoding = true;
                }

                UpdateStatus("New Section header.");
                SectionSize = SectionHeaders[2];
                Padding = false;
                CRC = 0;
            }
            else if ( ( AssembledBytes >= SectionStart + 3 ) && ( AssembledBytes == SectionStart + SectionSize + 3 ) )
            { // end of a section
                UpdateStatus("Section finished.");
                if ( SectionNumber < NumberOfSections )
                    UpdateStatus("Waiting for a new section.");

                SectionStart = SectionNumber * RS200_SECTION_LENGTH + 8;
                SectionNumber++;
                // don't record the remaining data, next relevant data will start after padding
                Padding = true;
            }
            if(AssembledBytes == FileSize) {
				if (thisByte == 7) {
                    UpdateStatus("Found trailer byte marking end of file.");
				}
                FinishedDecoding = true;
                ValidDecoding = true;
			}

            if ( FileSize > 0 )
            {
                double progress = ( (double) AssembledBytes / (double) FileSize ) * 100.0;
                UpdateProgress((int) progress);
            }
        }

        public void Save()
        {
            

        }

        private int SectionSize { get; set; }
        private int SectionNumber { get; set; }
        private int[] SectionHeaders = new int[3];
        private int SectionHeaderCount { get; set; }
        private ushort CRC { get; set; }
        private const int RS200_SECTION_LENGTH = 255;
        private int FileSize { get; set; }
        private int DataSize { get; set; }
        private int NumberOfSections { get; set; }
        private int AssembledBytes { get; set; }
        private bool Padding { get; set; }
        public bool FinishedDecoding { get; set; }
        public bool ValidDecoding { get; set; }
        public int Restart { get; set; }
        public int SectionStart { get; set; }
        public List<int> AssembledData { get; set; }
        MonitorTypeEnum MonitorType { get; set; }
    }
}
